package org.op.service.nav;

import java.io.File;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.swing.tree.DefaultMutableTreeNode;

import org.op.service.WwDocumentInfo;
import org.op.service.WwFolderInfo;
import org.op.service.WwInfoObject;


/**
 *  In this implementation of the NavigationService, the nodes are not cleared at each level
 *    They are compared with the file system, and nodes are added and removed as necessary
 *    
 *    -- add : exists in the filesystem, but not the model
 *    -- remove : exists in the model, but not the filesystem
 * 
 * @author david.peters
 *
 */
public class ReusingFileNavigationServiceImpl extends SimpleFileNavigationServiceImpl {
	

	/**
	 *   This node is synchronized with the filesystem (it exists, with the given name)
	 *   -- For each folder node, do a lookup and compare with the filesystem.
	 *	  --- If an existing child node does not exist, remove it
	 *    --- If a file does not exist as a node, add it
	 * 
	 * @param top
	 * @return
	 * @throws Exception 
	 */
	protected DefaultMutableTreeNode getExpandedNode(DefaultMutableTreeNode node) throws Exception {
		
		if (node.getUserObject() instanceof WwFolderInfo) {
			WwFolderInfo folderInfo = (WwFolderInfo) node.getUserObject();
			
			Map<String, DefaultMutableTreeNode> existingChildren = getExistingChildren(node);
			Map<String, File> fileChildren = getFileChildren(folderInfo.getFilepath());
			
			removeTreeNodes(existingChildren, fileChildren);
			node = addFileNodes(node, existingChildren, fileChildren);
			
			node = expandFolders(node);

		}
		
		// not necessary -- we haven't modified the parent
		return node;
	}

	@SuppressWarnings("unchecked")
	private DefaultMutableTreeNode expandFolders(DefaultMutableTreeNode node) throws Exception {
		
		Enumeration<DefaultMutableTreeNode> children = node.children();  // existing children
		while (children.hasMoreElements()) {
			DefaultMutableTreeNode child = children.nextElement();
			WwInfoObject infoObject = (WwInfoObject) child.getUserObject();
			if (infoObject instanceof WwFolderInfo) {
				child = getExpandedNode(child);
			}
		}
		
		// not necessary -- we havent modified the parent
		return node;
	}

	/**
	 *  Add nodes that exist in the filesystem, but not on the tree
	 * 
	 * @param parent
	 * @param existingChildren
	 * @param fileChildren
	 * @return
	 */
	private DefaultMutableTreeNode addFileNodes(DefaultMutableTreeNode parent, Map<String, DefaultMutableTreeNode> existingChildren, Map<String, File> fileChildren) {
		
		// boolean changed = false;
		// List<Integer> nodesAdded = new ArrayList<Integer>();
		int count = parent.getChildCount();
		
		Iterator<String> files = fileChildren.keySet().iterator();
		while (files.hasNext()) {
			String name = files.next();
			if (!existingChildren.containsKey(name)) {
				// new node -- add
				File file = fileChildren.get(name);
				DefaultMutableTreeNode newNode = createNodeFromFile(file);
				
				// parent.add(newNode);  // doesnt refresh properly
				
				// Have to go through all this nonsense to properly inform the tree listener
				getTreeModel().insertNodeInto(newNode, parent, count);
				// changed = true;
				// nodesAdded.add(new Integer(count));
				count++;
			}
		}
		
		/**
		if (changed) {
			int[] idx = new int[nodesAdded.size()];
			count=0;
			for (Integer nodesIdx : nodesAdded) {
				idx[count] = nodesIdx.intValue();
				count++;
			}
			getTreeModel().nodesWereInserted(parent, idx);
		}
		*/
		
		return parent;

	}

	/**
	 *  Create the node and add the associated info object
	 * 
	 * @param file
	 * @return
	 */
	private DefaultMutableTreeNode createNodeFromFile(File file) {
		
		String fileName = file.getName();
		boolean isFolder = file.isDirectory();
		String filePath = file.getAbsolutePath();

		if (!isFolder) {
			// hack file extension
			int i = fileName.indexOf(".");
			if (i>=0) {
				fileName = fileName.substring(0,i);
			}
		}
		
		DefaultMutableTreeNode node = new DefaultMutableTreeNode(fileName);
		WwInfoObject infoObject = null;
		if (isFolder) {
			infoObject = new WwFolderInfo(fileName, filePath);
		} else {
			infoObject = new WwDocumentInfo(fileName, filePath);
		}
		
		node.setUserObject(infoObject);
		return node;
		
	}

	/**
	 *  Remove all nodes that exist on the tree, but are not in the filesystem
	 * 
	 * @param node
	 * @param existingChildren
	 * @param fileChildren
	 * @return
	 */
	private void removeTreeNodes(Map<String, DefaultMutableTreeNode> existingChildren, Map<String, File> fileChildren) {
		
		Iterator<String> nodes = existingChildren.keySet().iterator();
		while (nodes.hasNext()) {
			String name = nodes.next();
			if (!fileChildren.containsKey(name)) {
				DefaultMutableTreeNode removalNode = existingChildren.get(name);
				
				getTreeModel().removeNodeFromParent(removalNode);
			}
		}

	}

	/**
	 *  Creates a map of the child files of this folder object in the local filesystem
	 * 
	 * @param filepath
	 * @return
	 * @throws Exception
	 */
	private Map<String, File> getFileChildren(String filepath) throws Exception {
		
		Map<String, File> fileChildren = new HashMap<String, File>();
		File parent = new File(filepath);
		if (parent.exists()) {		
			File[] files = parent.listFiles();
			for (File child: files) {
				String fileName = child.getName();
				// TODO make this configurable (whether or not to show hidden files)
				if (!fileName.startsWith(".")) {
					if (isValidWikiFile(child)) {
						// eliminate the file extension (what if two . in file name?
						int i = fileName.lastIndexOf('.');
						fileName = fileName.substring(0,i);
						fileName = FILE_PREPENDER + fileName;
						fileChildren.put(fileName, child);
					}
					if (child.isDirectory()) {
						fileName = FOLDER_PREPENDER + fileName;
						fileChildren.put(fileName, child);
					}
				}
			}
		}
		
		return fileChildren;

	}

}
